home *** CD-ROM | disk | FTP | other *** search
/ Gamers Delight 2 / Gamers Delight 2.iso / Aminet / game / role / pinfocom_3_0.lha / Source / termcap.c < prev    next >
C/C++ Source or Header  |  1992-10-22  |  30KB  |  1,236 lines

  1. /* termcap.c
  2.  *
  3.  *  ``pinfocom'' -- a portable Infocom Inc. data file interpreter.
  4.  *  Copyright (C) 1987-1992  InfoTaskForce
  5.  *
  6.  *  This program is free software; you can redistribute it and/or modify
  7.  *  it under the terms of the GNU General Public License as published by
  8.  *  the Free Software Foundation; either version 2 of the License, or
  9.  *  (at your option) any later version.
  10.  *
  11.  *  This program is distributed in the hope that it will be useful,
  12.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  *  GNU General Public License for more details.
  15.  *
  16.  *  You should have received a copy of the GNU General Public License
  17.  *  along with this program; see the file COPYING.  If not, write to the
  18.  *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  */
  20.  
  21. /*
  22.  * $Header: RCS/termcap.c,v 3.0 1992/10/21 16:56:19 pds Stab $
  23.  */
  24.  
  25. #include <stdio.h>
  26. #include <string.h>
  27. #include <ctype.h>
  28. #include <errno.h>
  29.  
  30. #include <signal.h>
  31.  
  32. #include "infocom.h"
  33.  
  34. #ifndef free
  35. extern void free();
  36. #endif
  37.  
  38. #ifdef NEED_ERRNO
  39. extern int errno;
  40. #endif
  41.  
  42. /*
  43.  * Terminal Capability Setup --
  44.  *
  45.  * Set up the terminal capability style to use.  Possibilities are
  46.  * TERMCAP, which almost every UNIX system has or TERMINFO, which
  47.  * newer systems have.
  48.  *
  49.  * Terminal capability is used to move the cursor around on the screen
  50.  * (to print the status line), to clear the screen, and to turn on and
  51.  * off standout mode(highlighting).
  52.  */
  53. #ifdef USE_TERMCAP
  54. #define TTY_PUT(_s)         tputs(_s,1,scr_putp)
  55. #define TTY_GOTO(_s,_c,_r)  tgoto(_s,_c,_r)
  56. #define TTY_PARM(_s,_1,_2)  tgoto(_s,_2,_1)
  57.  
  58. /*
  59.  * In newer systems these are defined in <term.h>, but I got a number
  60.  * of reports that many UNIX systems don't have a <term.h>, so I
  61.  * removed the #include and declare them here...
  62.  */
  63. extern int tgetent P((char*, char*));
  64. extern int tgetnum P((char*));
  65. extern int tputs P((char*, int, int(*) P((int))));
  66. extern char *tgoto P((char*, int, int));
  67. extern int ioctl P((int, int, ...));
  68.  
  69. static int
  70. scr_putp A1(int, c)
  71. {
  72.     return (putchar(c));
  73. }
  74. #endif
  75.  
  76.  
  77. #ifdef USE_TERMINFO
  78. #include <curses.h>
  79. #include <term.h>
  80.  
  81. #define TTY_PUT(_s)         putp(_s)
  82. #define TTY_GOTO(_s,_c,_r)  tparm(_s,_r,_c)
  83. #define TTY_PARM(_s,_1,_2)  tparm(_s,_1,_2)
  84.  
  85. extern int ioctl P((int, int, ...));
  86. #endif
  87.  
  88. /*
  89.  * Line Discipline Setup --
  90.  *
  91.  * Set up the line discipline to use.  TERMIOS is for POSIX-compliant
  92.  * systems, TERMIO is for SysVR3 and SysVR4 systems, and SGTTY is for
  93.  * BSD systems.  If none of these are set, then no line discipline
  94.  * manipulation is done.  The only thing line discipline is used for
  95.  * is for waiting for a character in "--More--" mode without printing
  96.  * it or waiting for a newline to read it.
  97.  */
  98. #ifdef USE_TERMIOS
  99. #include <termios.h>
  100.  
  101. #define TTY_TYPE        struct termios
  102. #define TTY_GET(_t)     tcgetattr(1, _t)
  103. #define TTY_SET(_t)     tcsetattr(1, TCSANOW, _t)
  104. #endif
  105.  
  106.  
  107. #ifdef USE_TERMIO
  108. #include <termio.h>
  109.  
  110. #define TTY_TYPE        struct termio
  111. #define TTY_GET(_t)     ioctl(1, TCGETA, _t)
  112. #define TTY_SET(_t)     ioctl(1, TCSETA, _t)
  113. #endif
  114.  
  115.  
  116. #ifdef USE_SGTTY
  117. #include <sgtty.h>
  118.  
  119. #define TTY_TYPE        struct sgttyb
  120. #define TTY_GET(_t)     gtty(1, _t)
  121. #define TTY_SET(_t)     stty(1, _t)
  122. #endif
  123.  
  124.  
  125. #ifndef TTY_TYPE
  126. #define TTY_TYPE        int
  127. #define TTY_GET(_t)
  128. #define TTY_SET(_t)
  129. #endif
  130.  
  131. extern char *getenv();
  132.  
  133. /*
  134.  * Global variables:
  135.  */
  136. #ifndef USE_READLINE
  137.  
  138. const char *scr_opt_list    = "";
  139. const char *scr_usage       = "";
  140. const char *scr_long_usage  = NULL;
  141.  
  142. #else
  143.  
  144. extern int tcrl_cmdarg P((int, char ***));
  145. extern void tcrl_getopt P((int, const char *));
  146. extern void tcrl_begin P((void));
  147. extern void tcrl_end P((void));
  148. extern void tcrl_pr_escape P((void));
  149. extern int scr_getstr P((const char *prompt,
  150.                          int length,
  151.                          char *buffer,
  152.                          Bool is_filenm));
  153.  
  154. const char *scr_opt_list    = "H:C:";
  155. const char *scr_usage       = "[-H file] [-C file]";
  156. const char *scr_long_usage  = "\
  157. \t-H file\tread/store command history in file\n\
  158. \t-C file\tread/store user command completions in file\n";
  159.  
  160. #endif
  161.  
  162.  
  163. /*
  164.  * Local variables:
  165.  */
  166. #define ENV_FLAGS       "PI_FLAGS"
  167.  
  168. #define CLREOL  (0)                         /* Clear to EOL */
  169. #define CLRSCR  (1)                         /* Clear screen */
  170. #define CURMOV  (2)                         /* Move cursor to position */
  171. #define HOME    (3)                         /* Home cursor */
  172. #define LOWLEF  (4)                         /* Mover cursor to lower-left */
  173. #define STDON   (5)                         /* Begin standout mode */
  174. #define STDOFF  (6)                         /* End standout mode */
  175. #define TERMON  (7)                         /* Begin termcap mode */
  176. #define TERMOFF (8)                         /* End termcap mode */
  177. #define SETSCRL (9)                         /* Set the scrolling region */
  178. #define MAX_OPS (10)
  179.  
  180.  
  181. static FILE *scr_fp=NULL;
  182.  
  183. static char *ops[MAX_OPS] = { "", "", "", "", "", "", "", "", "", "" };
  184. static char tbuffer[200];
  185.  
  186. static int scr_columns=80, scr_indent=0, stat_size=0, scr_width=80;
  187. static int scr_lines=24, scr_lineco=0, scr_lowleft=23;
  188.  
  189. static Bool allow_status=1, in_status=0;
  190.  
  191.  
  192. /*
  193.  * Function:    scr_cmdarg()
  194.  *
  195.  * Arguments:
  196.  *      argc        number of original arguments
  197.  *      argvp       pointer to array of strings containing args
  198.  *
  199.  * Returns:
  200.  *      Number of new arguments.
  201.  *
  202.  * Description:
  203.  *      This function is called before any command line parsing is
  204.  *      done.  If the interface needs to insert any interpreter
  205.  *      options into the command-line list then they should be added
  206.  *      here; otherwise the function should do nothing.  The should be
  207.  *      added between (*argvp)[0] and (*argvp)[1].  Note that
  208.  *      (*argvp)[0] must be the command name and (*argvp)[argc] must
  209.  *      be a null pointer.
  210.  *
  211.  * Notes:
  212.  */
  213. int
  214. scr_cmdarg A2(int, argc, char ***, argvp)
  215. {
  216.     char *envp;
  217.  
  218.     /*
  219.      * If we find an ENV_FLAGS environment variable then add the flags
  220.      * into the command line.
  221.      */
  222.     if ((envp = getenv(ENV_FLAGS)) != NULL)
  223.     {
  224.         char **newv;
  225.         char *cp;
  226.         int i, newc=0;
  227.  
  228.         /*
  229.          * Count the number of args in the cmd line
  230.          */
  231.         cp = envp;
  232.         do
  233.         {
  234.             while ((*cp != '\0') && isspace(*cp))
  235.                 ++cp;
  236.  
  237.             if (*cp != '\0')
  238.                 ++newc;
  239.  
  240.             while ((*cp != '\0') && !isspace(*cp))
  241.                 ++cp;
  242.         }
  243.         while (*cp != '\0');
  244.  
  245.         /*
  246.          * Allocate enough space for the old and new args
  247.          */
  248.         newv = (char **)xmalloc(sizeof(char *) * (argc+newc+1));
  249.         argc += newc;
  250.  
  251.         /*
  252.          * Copy the arguments into their proper places.
  253.          */
  254.         newv[0] = (*argvp)[0];
  255.         for (i=1, cp=envp; i<=newc; ++i)
  256.         {
  257.             while ((*cp != '\0') && isspace(*cp))
  258.                 ++cp;
  259.  
  260.             newv[i] = cp;
  261.  
  262.             while ((*cp != '\0') && !isspace(*cp))
  263.                 ++cp;
  264.             *(cp++) = '\0';
  265.         }
  266.  
  267.         for (; i<argc; ++i)
  268.             newv[i] = (*argvp)[i-newc];
  269.         newv[i] = NULL;
  270.  
  271.         *argvp = newv;
  272.     }
  273.  
  274. #ifdef USE_READLINE
  275.     argc = tcrl_cmdarg(argc, argvp);
  276. #endif
  277.  
  278.     return (argc);
  279. }
  280.  
  281.  
  282. /*
  283.  * Function:    scr_getopt()
  284.  *
  285.  * Arguments:
  286.  *      c           option found
  287.  *      arg         option argument (if requested)
  288.  *
  289.  * Description:
  290.  *      This function is called whenever a command-line option
  291.  *      specified in scr_opt_list (above) is found on the command
  292.  *      line.
  293.  */
  294. void
  295. scr_getopt A2(int, c, const char *, arg)
  296. {
  297. #ifdef USE_READLINE
  298.     tcrl_getopt(c, arg);
  299. #endif
  300. }
  301.  
  302.  
  303. /*
  304.  * Function:    scr_setup()
  305.  *
  306.  * Arguments:
  307.  *      margin      # of spaces in the right margin.
  308.  *      indent      # of spaces in the left margin.
  309.  *      scr_sz      # of lines on the screen.
  310.  *      context     # of lines of context to keep when scrolling
  311.  *
  312.  * Returns:
  313.  *      Width of screen output for informational printing (not used if
  314.  *      game is to be played).
  315.  *
  316.  * Description:
  317.  *      This function should set up generic items in the screen
  318.  *      interface that may need to be done before *any* output is
  319.  *      done.
  320.  *
  321.  *      If SCR_SZ is not 0, then this function must use SCR_SZ as the
  322.  *      number of lines the screen can hold at once, no matter what it
  323.  *      may infer otherwise.  If SCR_SZ is 0, then the function must
  324.  *      figure the size of the screen as best it can.
  325.  *
  326.  * Notes:
  327.  *      Any terminal initialization needed only for actually playing
  328.  *      the game should go in scr_begin(), not here.
  329.  */
  330. int
  331. scr_setup A4( int, margin,
  332.               int, indent,
  333.               int, scr_sz,
  334.               int, context )
  335. {
  336.     char *term, termbuf[1024];
  337.  
  338.     if ((term = getenv("TERM")) == NULL)
  339.     {
  340.         term = "dumb";
  341.     }
  342.  
  343. #ifdef USE_TERMCAP
  344.     if (tgetent(termbuf, term) == 1)
  345.     {
  346.         extern char *tgetstr P((char*, char**));
  347.         char *cp = tbuffer;
  348.  
  349.         if (((ops[STDON] = tgetstr("so", &cp)) == NULL) ||
  350.            ((ops[STDOFF] = tgetstr("se", &cp)) == NULL))
  351.             ops[STDON] = ops[STDOFF] = "";
  352.  
  353.         if (((ops[TERMON] = tgetstr("ti", &cp)) == NULL) ||
  354.            ((ops[TERMOFF] = tgetstr("te", &cp)) == NULL))
  355.             ops[TERMON] = ops[TERMOFF] = "";
  356.  
  357.         if ((ops[CLRSCR] = tgetstr("cl", &cp)) == NULL) ops[CLRSCR] = "";
  358.         if ((ops[CLREOL] = tgetstr("ce", &cp)) == NULL) ops[CLREOL] = "";
  359.         if ((ops[CURMOV] = tgetstr("cm", &cp)) == NULL) ops[CURMOV] = "";
  360.         if ((ops[HOME]   = tgetstr("ho", &cp)) == NULL) ops[HOME] = "";
  361.         if ((ops[LOWLEF] = tgetstr("ll", &cp)) == NULL) ops[LOWLEF] = "";
  362.         if ((ops[SETSCRL]= tgetstr("cs", &cp)) == NULL) ops[SETSCRL] = "";
  363.  
  364.         if ((scr_lines = tgetnum("li")) == -1) scr_lines=24;
  365.         if ((scr_columns = tgetnum("co")) == -1) scr_columns=80;
  366.     }
  367. #endif
  368.  
  369. #ifdef USE_TERMINFO
  370.     setupterm(term, 1, NULL);
  371.  
  372.     if (((ops[STDON] = tigetstr("smso")) == NULL) ||
  373.        ((ops[STDOFF] = tigetstr("rmso")) == NULL))
  374.         ops[STDON] = ops[STDOFF] = "";
  375.  
  376.     if ((ops[CLRSCR] = tigetstr("clear")) == NULL)  ops[CLRSCR] = "";
  377.     if ((ops[CLREOL] = tigetstr("el")) == NULL)     ops[CLREOL] = "";
  378.     if ((ops[CURMOV] = tigetstr("cup")) == NULL)    ops[CURMOV] = "";
  379.     if ((ops[HOME]   = tigetstr("home")) == NULL)   ops[HOME] = "";
  380.     if ((ops[LOWLEF] = tigetstr("ll")) == NULL)     ops[LOWLEF] = "";
  381.     if ((ops[SETSCRL]= tigetstr("csr")) == NULL)    ops[SETSCRL] = "";
  382.  
  383.     ops[TERMON] = enter_ca_mode;
  384.     ops[TERMOFF] = exit_ca_mode;
  385.     scr_lines = lines;
  386.     scr_columns = columns;
  387. #endif
  388.  
  389.     /*
  390.      * We must have either CURMOV or both HOME and LOWLEF in order to
  391.      * do status lines.  Since the pr_status flag can be changed, keep
  392.      * a local note that we can't do them regardless.
  393.      */
  394.     if ((*ops[CURMOV] == '\0')
  395.         && ((*ops[HOME] == '\0') || (*ops[LOWLEF] == '\0')))
  396.     {
  397.         gflags.pr_status = 0;
  398.         allow_status = 0;
  399.     }
  400.  
  401.     scr_columns -= margin + indent;
  402.     scr_width -= margin + indent;
  403.     scr_lowleft = scr_lines - 1;
  404.  
  405.     if (scr_sz)
  406.         scr_lines = scr_sz;
  407.  
  408.     scr_lines  -= context + (*ops[SETSCRL] != '\0') + 2;
  409.     scr_lineco -= context + (*ops[SETSCRL] != '\0') - 1;
  410.     scr_indent  = indent;
  411.  
  412.     return (scr_columns);
  413. }
  414.  
  415.  
  416. /*
  417.  * Function:    scr_shutdown()
  418.  *
  419.  * Description:
  420.  *      This function will be called just before we exit.
  421.  */
  422. void
  423. scr_shutdown()
  424. {
  425. }
  426.  
  427.  
  428. /*
  429.  * Function:    scr_begin()
  430.  *
  431.  * Arguments:
  432.  *      game    The game datafile we're about to execute.
  433.  *
  434.  * Description:
  435.  *      This function should perform terminal initializations we need
  436.  *      to actually play the game.
  437.  */
  438. void
  439. scr_begin()
  440. {
  441.     TTY_PUT(ops[TERMON]);
  442.     TTY_PUT(ops[CLRSCR]);
  443.  
  444.     /*
  445.      * If we have scrolling regions then set it and tell the
  446.      * interpreter we can handle status windows.
  447.      */
  448.     if (*ops[SETSCRL])
  449.     {
  450.         TTY_PUT(TTY_PARM(ops[SETSCRL], 1, scr_lowleft));
  451.         F1_SETB(B_STATUS_WIN);
  452.     }
  453.  
  454.     if (*ops[LOWLEF])
  455.         TTY_PUT(ops[LOWLEF]);
  456.     else if (*ops[CURMOV])
  457.         TTY_PUT(TTY_GOTO(ops[CURMOV], 0, scr_lowleft));
  458.     else
  459.         putchar('\n');
  460.  
  461. #ifdef USE_TERMINFO
  462.     reset_prog_mode();
  463. #endif
  464.  
  465. #ifdef USE_READLINE
  466.     tcrl_begin();
  467. #endif
  468. }
  469.  
  470.  
  471. static void
  472. scr_wait A1(const char *, prompt)
  473. {
  474.     TTY_TYPE orig;
  475.     TTY_TYPE new;
  476.     const char *outp = "\b \b";
  477.     const char *cp;
  478.  
  479.     /*
  480.      * Print the prompt,  flush the current input buffer of any
  481.      * typeahead (hopefully :-)
  482.      */
  483.     TTY_PUT(ops[STDON]);
  484.  
  485.     for (cp = prompt; *cp != '\0'; ++cp)
  486.         putchar(*cp);
  487.  
  488.     TTY_PUT(ops[STDOFF]);
  489.  
  490.     setbuf(stdin, NULL);
  491.  
  492.     /*
  493.      * Store the current terminal settings, then set up for no echo,
  494.      * one char at a time read.
  495.      */
  496.     TTY_GET(&orig);
  497.     new = orig;
  498.  
  499. #ifdef USE_SGTTY
  500.     new.sg_flags |= CBREAK;
  501.     new.sg_flags &= ~ECHO;
  502. #endif
  503. #if defined(USE_TERMIO) || defined(USE_TERMIOS)
  504.     new.c_lflag &= ~ICANON;
  505.     new.c_lflag &= ~ECHO;
  506.     new.c_cc[VMIN] = 1;
  507.     new.c_cc[VTIME] = 0;
  508. #endif
  509.  
  510.     signal(SIGINT, SIG_IGN);
  511.     TTY_SET(&new);
  512.  
  513.     /*
  514.      * Wait for a char...
  515.      */
  516.     getchar();
  517.  
  518.     /*
  519.      * Erase the prompt, reset the terminal, blah blah blah... we
  520.      * erase the prompt by using backspace/space/backspace chars
  521.      * instead of clear EOL or something because it's safer...
  522.      */
  523.     for (; *outp != '\0'; ++outp)
  524.     {
  525.         for (cp = prompt; *cp != '\0'; ++cp)
  526.         {
  527.             putchar(*outp);
  528.         }
  529.     }
  530.  
  531.     TTY_SET(&orig);
  532.     signal(SIGINT, askq);
  533. }
  534.  
  535.  
  536. /*
  537.  * Function:    scr_end()
  538.  *
  539.  * Description:
  540.  *      This function will be called after the last line is printed
  541.  *      but before we exit, *only* if scr_begin() was called (*not* if
  542.  *      just scr_startup() was called!)
  543.  */
  544. void
  545. scr_end()
  546. {
  547. #ifdef USE_READLINE
  548.     tcrl_end();
  549. #endif
  550.  
  551.     /*
  552.      * Some games exit without giving you a chance to read the last
  553.      * few lines, so we ask the user to type a char before we clear
  554.      * the screen...
  555.      */
  556.     scr_wait("--End--");
  557.  
  558.     if (*ops[SETSCRL])
  559.         TTY_PUT(TTY_PARM(ops[SETSCRL], 0, scr_lowleft));
  560.  
  561. #ifdef USE_TERMINFO
  562.     TTY_PUT(ops[CLRSCR]);
  563. #endif
  564.  
  565.     if (*ops[LOWLEF])
  566.         TTY_PUT(ops[LOWLEF]);
  567.     else if (*ops[CURMOV])
  568.         TTY_PUT(TTY_GOTO(ops[CURMOV], 0, scr_lowleft));
  569.     else
  570.         putchar('\n');
  571.  
  572. #ifdef USE_TERMINFO
  573.     reset_shell_mode();
  574. #endif
  575.  
  576.     TTY_PUT(ops[TERMOFF]);
  577. }
  578.  
  579. /*
  580.  * This is an internal function to print a buffer.  If flags==0 then
  581.  * print with paging and a final newline.  If flags & 1 then print
  582.  * without paging.  If flags & 2 then print up to but not including
  583.  * the last lineful, and return a pointer to it (a prompt).
  584.  */
  585. #define PB_NO_PAGING    (0x01)
  586. #define PB_PROMPT       (0x02)
  587.  
  588. static const char *
  589. scr_putbuf A4( FILE *, fp, int, flags, const char *, buf, int, max )
  590. {
  591.     const char *sp;
  592.     const char *ep;
  593.  
  594.     for (sp = buf; ;)
  595.     {
  596.         ep = chop_buf(sp, max);
  597.  
  598.         if ((*ep != '\0') || !(flags & PB_PROMPT))
  599.         {
  600.             const char *p;
  601.             int i;
  602.  
  603.             if (!(flags & PB_NO_PAGING) && (scr_lineco++ >= scr_lines))
  604.             {
  605.                 scr_wait("--More--");
  606.                 scr_lineco = 0;
  607.             }
  608.  
  609.             for (i=scr_indent; i>0; --i)
  610.                 putc(' ', fp);
  611.  
  612.             for (p = sp; p < ep; ++p)
  613.                 putc(*p, fp);
  614.  
  615.             putc('\n', fp);
  616.         }
  617.  
  618.         if (*ep == '\0')
  619.             break;
  620.  
  621.         sp = ep + 1;
  622.     }
  623.  
  624.     return (sp);
  625. }
  626.  
  627. /*
  628.  * Function:    scr_putline()
  629.  *
  630.  * Arguments:
  631.  *      buffer          Line to be printed.
  632.  *
  633.  * Description:
  634.  *      This function is passed a nul-terminated string and it should
  635.  *      display the string on the terminal.  It will *not* contain a
  636.  *      newline character.
  637.  *
  638.  *      This function should perform whatever wrapping, paging, etc.
  639.  *      is necessary, print the string, and generate a final linefeed.
  640.  *
  641.  *      If the TI supports proportional-width fonts,
  642.  *      F2_IS_SET(B_FIXED_FONT) should be checked as appropriate.
  643.  *
  644.  *      If the TI supports scripting, F2_IS_SET(B_SCRIPTING) should be
  645.  *      checked as appropriate.
  646.  */
  647. void
  648. scr_putline A1(const char *, buffer)
  649. {
  650.     scr_putbuf(stdout, in_status || !gflags.paged, buffer, scr_columns);
  651.  
  652.     if (F2_IS_SET(B_SCRIPTING))
  653.         scr_putbuf(scr_fp, 1, buffer, scr_width);
  654. }
  655.  
  656.  
  657. /*
  658.  * Function:    scr_putscore()
  659.  *
  660.  * Description:
  661.  *      This function prints the ti_location and ti_status strings
  662.  *      if it can and if status line printing is enabled.
  663.  */
  664. void
  665. scr_putscore()
  666. {
  667.     if (allow_status && gflags.pr_status)
  668.     {
  669.         extern char *ti_location;
  670.         extern char *ti_status;
  671.  
  672.         char    *ptr;
  673.         int     i;
  674.  
  675.         /*
  676.          * Go to the "home" position (top left corner) then clear to
  677.          * the EOL, then go into reverse video mode.
  678.          */
  679.         if (*ops[HOME])
  680.             TTY_PUT(ops[HOME]);
  681.         else if (*ops[CURMOV])
  682.             TTY_PUT(TTY_GOTO(ops[CURMOV], 0, 0));
  683.         else
  684.             putchar('\n');
  685.  
  686.         TTY_PUT(ops[CLREOL]);
  687.         TTY_PUT(ops[STDON]);
  688.  
  689.         for (ptr = ti_location; *ptr != '\0'; ++ptr)
  690.             putchar(*ptr);
  691.  
  692.         i = scr_columns+scr_indent - strlen(ti_location) - strlen(ti_status);
  693.         for (; i > 0; --i)
  694.             putchar(' ');
  695.  
  696.         for (ptr = ti_status; *ptr != '\0'; ++ptr)
  697.             putchar(*ptr);
  698.  
  699.         /*
  700.          * Turn of reverse video, then jump back down to the
  701.          * lower left corner of the screen.
  702.          */
  703.         TTY_PUT(ops[STDOFF]);
  704.         if (*ops[LOWLEF])
  705.             TTY_PUT(ops[LOWLEF]);
  706.         else if (*ops[CURMOV])
  707.             TTY_PUT(TTY_GOTO(ops[CURMOV], 0, scr_lowleft));
  708.         else
  709.             putchar('\n');
  710.     }
  711. }
  712.  
  713.  
  714. /*
  715.  * Function:    scr_putsound()
  716.  *
  717.  * Arguments:
  718.  *      number      sound number to play
  719.  *      action      action to perform
  720.  *      volume      volume to play sound at
  721.  *      argc        number of valid arguments
  722.  *
  723.  * Description:
  724.  *      This function plays the sound specified if it can; if not it
  725.  *      prints a line to that effect.
  726.  *
  727.  *      If the `argc' value is 1, then the we play `number' of beeps
  728.  *      (usually the ^G character).
  729.  *
  730.  *      If `argc' >1, the `action' argument is used as follows:
  731.  *
  732.  *          2:  play sound file
  733.  *          3:  stop playing sound file
  734.  *          4:  free sound resources
  735.  *
  736.  *      If `argc' >2, the `volume' argument is between 1 and 8 and is
  737.  *      a volume to play the sound at.
  738.  */
  739. void
  740. scr_putsound A4(int, number, int, action, int, volume, int, argc)
  741. {
  742.     if (argc == 1)
  743.     {
  744.         while (number--)
  745.             putchar('\a');
  746.     }
  747.     else if (argc == 3 && action == 2)
  748.     {
  749.         char buf[81];
  750.  
  751.         sprintf(buf, "Sound not supported! (number $%02x, volume %d)",
  752.                 number, volume);
  753.  
  754.         scr_putmesg(buf, 0);
  755.     }
  756. }
  757.  
  758.  
  759. /*
  760.  * Function:    scr_putmesg()
  761.  *
  762.  * Arguments:
  763.  *      buffer      message string to be printed.
  764.  *      is_err      1 if message is an error message, 0 if it's not.
  765.  *
  766.  * Description:
  767.  *      This function prints out a message from the interpreter, not
  768.  *      from the game itself.  Often these are errors (IS_ERR==1)
  769.  *      but not necessarily.
  770.  */
  771. void
  772. scr_putmesg A2(const char *, buffer, Bool, is_err)
  773. {
  774.     static char *buf = "";
  775.     int blen;
  776.  
  777.     blen = strlen(buffer);
  778.     if (strlen(buf) < blen + 12)
  779.     {
  780.         if (strlen(buf) == 0)
  781.             buf = xmalloc(blen + 13);
  782.         else
  783.             buf = xrealloc(buf, blen + 13);
  784.     }
  785.  
  786.     sprintf(buf, " [ %s%s ]",
  787.             is_err ? "ERROR: " : "",
  788.             buffer);
  789.  
  790.     scr_putline("");
  791.     scr_putline(buf);
  792.     scr_putline("");
  793. }
  794.  
  795.  
  796. #ifndef USE_READLINE
  797. /*
  798.  * Function:    scr_getstr()
  799.  *
  800.  * Description:
  801.  *      This is an internal function which performs the meat of
  802.  *      reading a string, throwing away the excess, etc.  It returns
  803.  *      the number of characters read.
  804.  *
  805.  * Notes:
  806.  *      This function is *not* a part of the terminal interface; it's
  807.  *      just an interal helper function.
  808.  */
  809. static int
  810. scr_getstr A4( const char *, prompt,
  811.                int, length,
  812.                char *, buffer,
  813.                Bool, is_filenm )
  814. {
  815.     int c;
  816.  
  817.     fputs(prompt, stdout);
  818.     fflush(stdout);
  819.  
  820.     buffer[0] = '\0';
  821.     buffer[length-2] = '\0';
  822.     fgets(buffer, length, stdin);
  823.  
  824.     /*
  825.      * If there's more beyond the max length of our buffer, read
  826.      * it in and throw it away...
  827.      */
  828.     if ((buffer[length-2] != '\0')
  829.         && (buffer[length-2] != '\n')
  830.         && ((c = getchar()) != EOF)
  831.         && (c != '\n'))
  832.     {
  833.         fputs(" [ Input line too long.  Flushing: `", stdout);
  834.         do
  835.         {
  836.             putchar(c);
  837.         }
  838.         while (((c = getchar()) != EOF) && (c != '\n'));
  839.  
  840.         scr_putline("' ]");
  841.     }
  842.     else
  843.     {
  844.         /*
  845.          * Punt the last \n...
  846.          */
  847.         length = strlen(buffer) - 1;
  848.         buffer[length] = '\0';
  849.     }
  850.  
  851.     return (length);
  852. }
  853. #endif
  854.  
  855.  
  856. /*
  857.  * Function:    scr_getline()
  858.  *
  859.  * Arguments:
  860.  *      prompt    - prompt to be printed
  861.  *      length    - total size of BUFFER
  862.  *      buffer    - buffer to return nul-terminated response in
  863.  *
  864.  * Returns:
  865.  *      # of chars stored in BUFFER
  866.  *
  867.  * Description:
  868.  *      Reads a line of input and returns it.  Handles all "special
  869.  *      operations" such as readline history support, shell escapes,
  870.  *      etc. invisibly to the caller.  Note that the returned BUFFER
  871.  *      will be at most LENGTH-1 chars long because the last char will
  872.  *      always be the nul character.
  873.  *
  874.  *      If the command begins with ESC_CHAR then it's an interpreter
  875.  *      escape command; call ti_escape() with the rest of the line,
  876.  *      then ask for another command.
  877.  *
  878.  * Notes:
  879.  *      May print the STATUS buffer more than once if necessary (i.e.,
  880.  *      a shell escape messed up the screen, a history listing was
  881.  *      generated, etc.).
  882.  */
  883. int
  884. scr_getline A3( const char *, prompt,
  885.                 int,          length,
  886.                 char *,       buffer )
  887. {
  888.     extern int  system P((const char *));
  889.  
  890.     const char *pp;
  891.     int len;
  892.  
  893.     /*
  894.      * Loop until we've read a line.  Note that shell commands don't
  895.      * count, so if we get one then read another line.
  896.      */
  897.     for (;;)
  898.     {
  899.         /*
  900.          * Print all the prompt except the last line
  901.          */
  902.         pp = scr_putbuf(stdout, PB_PROMPT, prompt, scr_columns);
  903.  
  904.         scr_putscore();
  905.         scr_lineco=0;
  906.  
  907.         buffer[0] = '\0';
  908.         buffer[length-1] = '\0';
  909.         buffer[length-2] = '\0';
  910.  
  911.         if ((len = scr_getstr(pp, length, buffer, 0)) == -1)
  912.             continue;
  913.  
  914.         if (!len)
  915.             break;
  916.  
  917.         /*
  918.          * If it's an interpreter escape, call the function then try
  919.          * again.
  920.          */
  921.         if (buffer[0] == ESC_CHAR[0])
  922.         {
  923.             ti_escape(&buffer[1]);
  924. #ifdef USE_READLINE
  925.             if (len == 1)
  926.                 tcrl_pr_escape();
  927. #endif
  928.             scr_putscore();
  929.             continue;
  930.         }
  931.  
  932. #ifdef NO_SHELL_ESC
  933.         break;
  934. #else
  935.         /*
  936.          * If it's not a shell escape, we've got a valid string so
  937.          * quit.
  938.          */
  939.         if (buffer[0] != '!')
  940.         {
  941.             break;
  942.         }
  943.  
  944.         /*
  945.          * Otherwise execute the shell command: if no command was
  946.          * given invoke the user's $SHELL (or /bin/sh if $SHELL is not
  947.          * found).
  948.          */
  949.         if (len == 1)
  950.         {
  951.             char *cp;
  952.  
  953.             strncpy(&buffer[1],
  954.                     (cp=getenv("SHELL"))==0 || *cp=='\0' ? "/bin/sh" : cp,
  955.                     length-2);
  956.         }
  957.  
  958. #ifdef USE_TERMINFO
  959.         reset_shell_mode();
  960.         TTY_PUT(ops[TERMOFF]);
  961. #endif
  962.  
  963.         system(&buffer[1]);
  964.  
  965. #ifdef USE_TERMINFO
  966.         TTY_PUT(ops[TERMON]);
  967.         reset_prog_mode();
  968. #endif
  969.         putchar('\n');
  970.  
  971. #endif  /* !NO_SHELL_ESC */
  972.     }
  973.  
  974.     if (F2_IS_SET(B_SCRIPTING))
  975.     {
  976.         pp = scr_putbuf(scr_fp, PB_NO_PAGING|PB_PROMPT, prompt, scr_width);
  977.         fputs(pp, scr_fp);
  978.         scr_putbuf(scr_fp, PB_NO_PAGING, buffer, scr_width);
  979.     }
  980.  
  981.     return (len);
  982. }
  983.  
  984.  
  985. /*
  986.  * Function:    scr_window()
  987.  *
  988.  * Arguments:
  989.  *      size      - 0 to delete, non-0 means create with SIZE.
  990.  *
  991.  * Description:
  992.  *      Causes a status window to be created if supported by the
  993.  *      terminal interface; note this function won't be called unless
  994.  *      F1_SETB(B_STATUS_WIN) is invoked in scr_begin().
  995.  *
  996.  * Notes:
  997.  */
  998. void
  999. scr_window A1(int, size)
  1000. {
  1001.     char *p;
  1002.     int i;
  1003.  
  1004.     if (!size)
  1005.     {
  1006.         if (!stat_size)
  1007.             return;
  1008.  
  1009.         scr_lines += stat_size;
  1010.         stat_size = 0;
  1011.  
  1012.         p = TTY_PARM(ops[SETSCRL], 1, scr_lowleft);
  1013.         TTY_PUT(p);
  1014.         scr_set_win(0);
  1015.  
  1016.         return;
  1017.     }
  1018.  
  1019.     stat_size = size;
  1020.     scr_lines -= stat_size;
  1021.     p = TTY_PARM(ops[SETSCRL], stat_size+1, scr_lowleft);
  1022.     TTY_PUT(p);
  1023.  
  1024.     /*
  1025.      * Clean out the window
  1026.      */
  1027.     scr_set_win(1);
  1028.     for (i=stat_size; i>0; --i)
  1029.     {
  1030.         if (*ops[CLREOL])
  1031.             TTY_PUT(ops[CLREOL]);
  1032.         else
  1033.         {
  1034.             int j;
  1035.  
  1036.             for (j=scr_columns; j>0; --j)
  1037.                 putchar(' ');
  1038.         }
  1039.         putchar('\n');
  1040.     }
  1041.     scr_set_win(0);
  1042. }
  1043.  
  1044.  
  1045. /*
  1046.  * Function:    scr_set_win()
  1047.  *
  1048.  * Arguments:
  1049.  *      win       - 0==select text window, 1==select status window
  1050.  *
  1051.  * Description:
  1052.  *      Selects a different window.  This function won't be called
  1053.  *      unless call F1_SETB(B_STATUS_WIN) in scr_begin().
  1054.  *
  1055.  * Notes:
  1056.  */
  1057. void
  1058. scr_set_win A1(int, win)
  1059. {
  1060.     /*
  1061.      * Select the status window; just move the cursor there
  1062.      */
  1063.     if (win)
  1064.     {
  1065.         in_status = 1;
  1066.  
  1067.         if (*ops[CURMOV])
  1068.             TTY_PUT(TTY_GOTO(ops[CURMOV], 0, 1));
  1069.         else if (*ops[HOME])
  1070.         {
  1071.             TTY_PUT(ops[HOME]);
  1072.             putchar('\n');
  1073.         }
  1074.     }
  1075.     else
  1076.     {
  1077.         in_status = 0;
  1078.  
  1079.         if (*ops[LOWLEF])
  1080.             TTY_PUT(ops[LOWLEF]);
  1081.         else if (*ops[CURMOV])
  1082.             TTY_PUT(TTY_GOTO(ops[CURMOV], 0, scr_lowleft));
  1083.     }
  1084. }
  1085.  
  1086.  
  1087. /*
  1088.  * Function:    scr_open_sf()
  1089.  *
  1090.  * Arguments:
  1091.  *      length    - total size of BUFFER
  1092.  *      buffer    - buffer to return nul-terminated filename in
  1093.  *      type      - SF_SAVE     opening the file to save into
  1094.  *                  SF_RESTORE  opening the file to restore from
  1095.  *                  SF_SCRIPT   opening a file for scripting
  1096.  *
  1097.  * Returns:
  1098.  *      FILE* - reference to the opened file, or
  1099.  *      NULL  - errno==0: operation cancelled, else error opening file
  1100.  *
  1101.  * Description:
  1102.  *      Obtains the name of the file to be opened for writing (if
  1103.  *      TYPE==SF_SAVE or SF_SCRIPT) or reading (if TYPE==SF_RESTORE),
  1104.  *      opens the file with fopen(), and returns the FILE*.
  1105.  *
  1106.  *      The name of the file should be stored in BUFFER.  Upon initial
  1107.  *      calling BUFFER contains a possible default filename.
  1108.  *
  1109.  *      if LENGTH==0 then don't ask the user for a name, just use
  1110.  *      BUFFER.  This means, for example, we got the -r option to
  1111.  *      restore the file.
  1112.  *
  1113.  *      If the fopen() fails just return NULL: if errno!=0 then an
  1114.  *      error will be printed.
  1115.  *
  1116.  * Notes:
  1117.  *      History is turned off here (why would anyone want it?)
  1118.  */
  1119. FILE *
  1120. scr_open_sf A3( int, length, char *, buffer, int, type )
  1121. {
  1122. #define FN_PROMPT       "Filename (or q)"
  1123.  
  1124.     FILE *fp;
  1125.     char prompt[MAXPATHLEN+sizeof(FN_PROMPT)+6];
  1126.     char *cp, *p;
  1127.     int len;
  1128.  
  1129.     if (length == 0)
  1130.     {
  1131.         errno = 0;
  1132.         return (fopen(buffer,
  1133.                       type==SF_RESTORE ? "rb" : type==SF_SAVE ? "wb" : "w"));
  1134.     }
  1135.  
  1136.     cp = xmalloc(length);
  1137.  
  1138.  retry:
  1139.     scr_putscore();
  1140.     scr_lineco=0;
  1141.  
  1142.     sprintf(prompt, "%s [%s] ? ", FN_PROMPT, buffer);
  1143.  
  1144.     len = scr_getstr(prompt, length, cp, 1);
  1145.  
  1146.     /*
  1147.      * Remove any excess whitespace from the end of the string.
  1148.      * If the input is really empty, then use the initial buffer.
  1149.      * If not copy over the string into the buffer.
  1150.      */
  1151.     for (p = &cp[len-1]; len && isspace(*p); --len, --p)
  1152.     {}
  1153.     p[1] = '\0';
  1154.  
  1155.     fp = NULL;
  1156.     errno = 0;
  1157.  
  1158.     if ((len == 1) && (cp[0] == 'q'))
  1159.         goto done;
  1160.  
  1161.     if (len)
  1162.         strcpy(buffer, cp);
  1163.  
  1164.     len = strlen(buffer);
  1165.  
  1166.     /*
  1167.      * Open the file for reading.  If we're saving and it already
  1168.      * exists then ask if the user wants to overwrite it.  If not then
  1169.      * ask for another name.
  1170.      */
  1171.     if (len)
  1172.     {
  1173.         fp = fopen(buffer, "rb");
  1174.         if (type != SF_RESTORE)
  1175.         {
  1176.             if (fp != NULL)
  1177.             {
  1178.                 char over_p[MAXPATHLEN+40];
  1179.  
  1180.                 fclose(fp);
  1181.                 sprintf(over_p,
  1182.                         "File `%s' exists: overwrite (y/n/q)[y]? ",
  1183.                         buffer);
  1184.  
  1185.                 if (scr_getstr(over_p, 81, cp, 1))
  1186.                     if (cp[0] == 'q')
  1187.                     {
  1188.                         fp = NULL;
  1189.                         goto done;
  1190.                     }
  1191.                     else if (cp[0] != 'y')
  1192.                         goto retry;
  1193.             }
  1194.  
  1195.             fp = fopen(buffer, type==SF_SAVE ? "wb" : "w");
  1196.         }
  1197.     }
  1198.  
  1199.  done:
  1200.     free(cp);
  1201.  
  1202.     if (type == SF_SCRIPT)
  1203.         scr_fp = fp;
  1204.  
  1205.     return (fp);
  1206. }
  1207.  
  1208.  
  1209. /*
  1210.  * Function:    scr_close_sf()
  1211.  *
  1212.  * Arguments:
  1213.  *      filenm    - name of file just processed
  1214.  *      fp        - FILE* to open saved file
  1215.  *      type      - SF_SAVE     closing a saved game file
  1216.  *                  SF_RESTORE  closing a restored game file
  1217.  *                  SF_SCRIPT   closing a scripting file
  1218.  *
  1219.  * Description:
  1220.  *      This function will be called immediately after a successful
  1221.  *      save or restore of a game file, so that if the interface needs
  1222.  *      to perform any actions related to the saved game it may.  It
  1223.  *      will also be called when the interpreter notices that
  1224.  *      scripting has been turned off.  It should at least close the
  1225.  *      file.
  1226.  *
  1227.  *      This function will only be called if the save/restore of the
  1228.  *      game succeeded; if it fails the file will be closed by the
  1229.  *      interpreter.
  1230.  */
  1231. void
  1232. scr_close_sf A3( const char *, filenm, FILE *, fp, int, type )
  1233. {
  1234.     fclose(fp);
  1235. }
  1236.